#!/usr/bin/env rdmd

// ue.d is UE Editor


/+
Mode            Scancode   Mod+Scancode // mod=any modifier other than super, be it ctrl, alt, or esc_prefix

Kbd Setup Mode
Function Mode
Input Mode

Default key config:


[ABCDEFGHIJKL
`1234567890[]
aoeuidhtns-HD HUE
;qjkxbmwvz\J  LDR

Mode 0:

Esc
^ F#    define macro to function key
^F#     play macro to function key
^[1-9]  switch to mode #
^0

-------------------------------------------------------------
| ' | , | . |buf|

gototag
fold
unfold







+/

import     std.      stdio       ;
/// import               consoled    ;
import a = core.stdc.stdio       ;
import     std.c.    stdlib      ;
import     std.      file        ;
import     std.      conv    : to;

/// //import core.sys.posix.sys.ioctl;

//import qmoduleq_e;
import qmoduleq_g;
import qmoduleq_i;
import qmoduleq_o;
//import qmoduleq_r;
//import qmoduleq_w;
//import qmoduleq_g;

import module_ue_ue_die;
import module_ue_ue_numcon;

/+ in module_ue_ue_numcon.d iff anywhere
immutable char[16] HEX = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
immutable char[16] hEX = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
+/

string TITLE = "ue: %t [%l/%L]%F in %p";

/+ in module_ue_ue_die.d
bool DIE = false;
string MSG = "";
int RET = 0;
+/

int W = 80;
int H = 24;

int main(string[] arg)
{
/+
 scope(failure) return RET;
 scope(exit) write(MSG);
 scope(exit) clearScreen();
 scope(exit) resetColors();
 scope(exit) resetFontStyle();
/// // scope(exit) mode = ConsoleInputMode(true, true); // CONFIRMED SEGMENTATION FAULT LINE HERE!!
///  scope(exit) cursorVisible = true;
+/
///  addCloseHandler((i)
///  {
///   switch(i.type)
///   {
///    case CloseType.Other:     // Other
///     die(0, "Good bye. (??)");
///     break;
///    case CloseType.Interrupt: // ^C
///     die(0, "Good bye. (^C)");
///     break;
///    case CloseType.Stop:      // ^Z or M$ ^Break
///     die(0, "Good bye. (^Z)");
///     break;
///    case CloseType.Quit:      // ^\\
///     die(0, "Good bye. (^\\)");
///     break;
///    default:
///     die(-2, "setCloseHandler's i.type hit default");
///     break;
///   }
///  });
/// 
///  cursorVisible = false;
/// ////// mode = ConsoleInputMode.None; // CONFIRMED CAUSING SEGV IN NEXT VARIABLE DECLARATION LINE
W = cast(int)size[0];
H = cast(int)size[1];
 clrs();
 bool   cjkascii = true;
 string zcjkpreviewsize = "16";
 bool   colornotreversevideo = true;
 string[12] macro_fn = "";
 bool   usejustification = false;
 string zjustificationstops = "3";
 bool   keywordhighlighting = true;
 string keywordfile = "D";
 string zkeyboardlayoutpreset = "0"; // 0 for dsk, 1 for qwerty, ..., 255 for custom
 string keyboardlayoutfile = "";
 bool   saveoriginals = true;
 string saveoriginalpath = "/tmp/ue";
 bool   strictreadonlymode = false;
 bool   usespacesnottabs = true;
 string ztabstopwidth = "4";
// bool   documentisunicode = detectunicodedocument();
 string writabilitycommand = "";
 for(auto i = 1; i < arg.length - 1; i++)
 {
  switch(arg[i])
  {
   case "-a":
    cjkascii = true;
    zcjkpreviewsize = arg[++i];
    break;
   case "-A":
    cjkascii = false;
    break;
   case "-c":
    colornotreversevideo = true;
    break;
   case "-C":
    colornotreversevideo = false;
    break;
   case "-f1":
    macro_fn[0] = arg[++i];
    break;
   case "-f2":
    macro_fn[1] = arg[++i];
    break;
   case "-f3":
    macro_fn[2] = arg[++i];
    break;
   case "-f4":
    macro_fn[3] = arg[++i];
    break;
   case "-f5":
    macro_fn[4] = arg[++i];
    break;
   case "-f6":
    macro_fn[5] = arg[++i];
    break;
   case "-f7":
    macro_fn[6] = arg[++i];
    break;
   case "-f8":
    macro_fn[7] = arg[++i];
    break;
   case "-f9":
    macro_fn[8] = arg[++i];
    break;
   case "-f10":
    macro_fn[9] = arg[++i];
    break;
   case "-f11":
    macro_fn[10] = arg[++i];
    break;
   case "-f12":
    macro_fn[11] = arg[++i];
    break;
   case "--help":
   case "-h":
    writeln(q"HELPSTRING
Flags:
-a#  use romanization and ascii art for UTF-8, and use a maximum CJK preview size of 2*# (w) by # (h)
-A   use UTF-8 directly
-c   use color (default)
-C   use reverse video
--help,
-h   display this help text
-k@  enable keyword highlighting using dictionary @ (@="/path/to/dictionaryFile", or @="D" for D2) (default=D)
-K   disable keyword highlighting
-l#  use preset keyboard layout #
-L'' use custom keyboard layout ''
-o@  enable saving originals in @/file.process.time.bak (default="/tmp/ue")
-O   disable saving originals
-r   enable strict read-only mode
-R   disable strict read-only mode (default)
-t#  define tab stop to # spaces and use spaces not tabs (default=4)
-T#  define tab stop to # spaces and use tabs not spaces
-j#  enable justification to # number of tab stops (overrides -t and -T)
// ((.?[^\t])*)\t\t(([^\t](.?[^\t]))*)(.*) -> \1=L,\3=C,\6=R
// ((.?[^\t])*)\t\t\t(.*)                  -> \1=L,     \3=R
-J   disable justification (default)
-u   treat file as UTF-8 (default unless file contains invalid utf-8 encodings)
-U   treat file as ASCII (default if file contains invalid utf-8 encodings)
-w'' writability command
HELPSTRING"
     );
    break;
   case "-j":
    usejustification = true;
    zjustificationstops = arg[++i];
    break;
   case "-J":
    usejustification = false;
    break;
   case "-k":
    keywordhighlighting = true;
    keywordfile = arg[++i];
    break;
   case "-K":
    keywordhighlighting = false;
    break;
   case "-l":
    zkeyboardlayoutpreset = arg[++i];
    break;
   case "-L":
    zkeyboardlayoutpreset = "255";
    keyboardlayoutfile = arg[++i];
    break;
   case "-o":
    saveoriginals = true;
    saveoriginalpath = arg[++i];
    break;
   case "-O":
    saveoriginals = false;
    break;
   case "-r":
    strictreadonlymode = true;
    break;
   case "-R":
    strictreadonlymode = false;
    break;
   case "-t":
    usejustification = false;
    usespacesnottabs = true;
    ztabstopwidth = arg[++i];
    break;
   case "-T":
    usejustification = false;
    usespacesnottabs = false;
    ztabstopwidth = arg[++i];
    break;
   case "-u":
    writeln("UTF-8 mode ENABLED");
    break;
   case "-U":
    writeln("UTF-8 mode DISABLED");
    break;
   case "-w":
    writabilitycommand = arg[++i];
    break;
   default:
//    die(1, "WARNING: Unrecognized commandline switch ``" ~ arg[i] ~ "''");
    break;
  }
 }
 ubyte  cjkpreviewsize = to!(ubyte)(zcjkpreviewsize);
 ubyte  justificationstops = to!(ubyte)(zjustificationstops);
 ubyte  keyboardlayoutpreset = to!(ubyte)(zkeyboardlayoutpreset);
 ubyte  tabstopwidth = to!(ubyte)(ztabstopwidth);
 writeln("File: ", arg[$ - 1]);

 for(auto i = 0; i < 12; i++)
 {
  writeln("Macro F", i + 1, ": ", macro_fn[i]);
 }
 writeln("Size: W=",W,", H=",H);

 ubyte mode0 = 0x7f;
 while(!DIE)
 {
  setCursorPos(0, 24);
  title = TITLE;
  setCursorPos(0, 0);
  switch(mode0)
  {
   case 0x00:
    mode0 = loop();
    break;
   case 0x7f:
//    mode0 = keys();
    break;
   case 0x80: // to 0x00
    // <-- adjust size
    mode0 = clrs() ? 0x80 : 0x00;
    // determine width and height (so resize merely needs to mode0=0x80), and draw text once.
    break;
   case 0xff: // to 0x7f
    // <-- adjust size
    mode0 = clrs() ? 0xff : 0x7f;
    // draw the grid
    break;
   default:
    die(-1, "Error: mode0 hit default");
    break;
  }
 }
 dead:
 cursorVisible = true;
// mode = ConsoleInputMode(true, true); // CONFIRMED SEGMENTATION FAULT ON EXIT HERE!!!!!!
 resetFontStyle();
 resetColors();
 clearScreen();
 write(MSG);
 return RET;
} //{\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  main }

/+
struct GC_Key
{
 public:
  this(int ich_, int mask_OPGACS_s, int raw_, string str_, string buf_)
  {
   ich =             ich_;
   ch  = cast(dchar) ich_;
   mask= cast(ubyte) mask_OPGACS_s;
   raw =             raw;
   str =             str_;
   buf =             buf_;
  }
  int ich;
  dchar ch;
  ubyte mask;
  int raw;
  string str;
  string buf;
};

enum GC_SpecialKey
{
 home = 0x0200,
 pgup,
 pgdn,
 endl,
 del,
 ins,
 up,
 dn,
 fw,
 bk,
 esc = 0x1b,
 tab = 0x09,
}

GC_SpecialKey[string] GC_specialKeys;
+/

bool clrs()
{ // clears screen and returns whether or not the width/height have changed
 clearScreen();
 return (W == size[0] && H == size[1]);
}

ubyte loop() // 0x00
{
 return 0x00;
}

/+
ubyte keys() // 0x7f
{
 GC_Key G;
 int g;
 setCursorPos(0, 0);
 G = gc();//clrs();//getchar
 g = cast(int)G.ch;
 writeln("[~ ", g, " ~]");
 writeln("_G: \".{ich=",G.ich,",ch=",G.ch,",mask=",G.mask,",raw=",G.raw,",str=",G.str,",buf=",G.buf,"}.\"");
 ungetc(g, stdin.getFP);
 G = gc();//getch
 g = cast(int)G.ch;
 writeln("G_: \".{ich=",G.ich,",ch=",G.ch,",mask=",G.mask,",raw=",G.raw,",str=",G.str,",buf=",G.buf,"}.\"");
 while(!DIE&&cast(char)getchar() != ' ') { }
 clrs();// if(g == 0x0A || g == 0x0D)
//  return akey();
 switch(g) // NOTE: sort by order of frequency of use!!
 {
  case 0:write("-");break;
  case 0x20: // Space 0x20
   writeln("<space>");
   break;
  case 0x0a: // Enter 0x0a
   writeln("\\n");
   break;
  case 0x0206: // Up
   writeln("U");
   break;
  case 0x0207: // Dn
   writeln("D");
   break;
  case 0x0209: // Fw
   writeln("F");
   break;
  case 0x0208: // Bk
   writeln("B");
   break;
  case 0x0201: // PgUp
   writeln("PgUp");
   break;
  case 0x0202: // PgDn
   writeln("PgDn");
   break;
  default:
   writefln("0x%08x -> '%c' <- \"%s\"", g, cast(char)g, g);
   break;
  }
 return 0x7f;
}
ubyte akey()
{
 // <-- apply keys
 return 0x80;
}

//alias Tuple!("dchar", ch, "ubyte", mask_OPGACS_s, "int", raw, "string", str, "string", buf) GC_Key;
//alias Tuple!(dchar, ubyte, int, string, string) GC_Key;

GC_Key gc(bool echo = false)
{
 int ch;
 int raw;
 string str;
 int mask_all;
 string buf;
 bool is_special; // s
 bool is_unknown; //  |
 bool mask_shift; // S
 bool mask_ctrl;  // C
 bool mask_alt;   // A
 bool mask_altgr; // G
 bool mask_super; // P
 bool mask_other; // O
 ConsoleInputMode m;

 m = mode;
 mode = ConsoleInputMode(echo, false);
 ch = raw = getchar();

 if(ch == GC_SpecialKey.esc)
 {
  is_special = true;
  while(kbhit())
  {
   buf ~= getchar();
  }
  writeln(buf);
  if(buf in GC_specialKeys)
  {
   ch = GC_specialKeys[buf];
   writeln("G; ch = [ ", ch, " ] buf = [ ", buf, " ]");
  }
  else
  {
   writeln("e; ch = [ ", ch, " ] buf = [ ", buf, " ]");
   ch = -1;
   is_unknown = true;
  }
 }
 else
 {
  is_special = false;
  is_unknown = false;
  mask_ctrl  = false;
  mask_alt   = false;
  mask_altgr = false;
  mask_super = false;
  mask_other = false;
 }
 mask_all = (is_special ? 0b0000_0001 : 0) + (is_unknown ? 0b0000_0010 : 0)
          + (mask_shift ? 0b0000_0100 : 0) + (mask_ctrl  ? 0b0000_1000 : 0)
          + (mask_alt   ? 0b0001_0000 : 0) + (mask_altgr ? 0b0010_0000 : 0)
          + (mask_super ? 0b0100_0000 : 0) + (mask_other ? 0b1000_0000 : 0);
 writeln(">-> \".{ch=",ch,",mask_all=",mask_all,",raw=",raw,",str=",str,",buf=",buf,"}.\"");
 auto ret = GC_Key(ch, mask_all, raw, str, buf);

 mode = m;
// writeln("ret: \".{ich=",ret.ich,",ch=",ret.ch,",mask=",ret.mask,",raw=",ret.raw,",str=",ret.str,",buf=",ret.buf,"}.\"");
// while(!DIE&&cast(char)getchar() != ' ') { }
// clrs();
 return ret;
}
+/

/+ in module_ue_ue_die.d
void die(int ret, string message)
{
 writefln(ret == 0 ? "%s" : "Error:  %s (%i)", message, message, ret);
 if(ret <= 0)
  DIE = true;
 MSG ~= (ret == 0 ? "QUIT [" : (ret < 0 ? "ERROR [" : "WARNING [")) ~ to!(string)(RET) ~ "]: " ~ message ~ "\n"; //]]
 RET = ret;
 return;
}
+/
